// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// q [author] rhyme_lph
// S [email]  rhymelph@gmail.com
// F [github] https://github.com/rhymelph

import 'dart:async';

import 'package:base_plugin/base_plugin.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/business_contact_entity.dart';
import 'package:finance_app/src/entities/invoice_detail_entity.dart';
import 'package:finance_app/src/entities/invoice_entity.dart';
import 'package:finance_app/src/entities/ocr_attachment_entity.dart';
import 'package:finance_app/src/entities/payment_term_entity.dart';
import 'package:finance_app/src/entities/quotation_detail_entity.dart';
import 'package:finance_app/src/entities/quotation_entity.dart';
import 'package:finance_app/src/entities/setting_data_entity.dart';
import 'package:finance_app/src/http_service/http_invoice_service.dart';
import 'package:finance_app/src/mixin/apply_credit_mixin.dart';
import 'package:finance_app/src/mixin/check_plan_mixin.dart';
import 'package:finance_app/src/mixin/discount_mixin.dart';
import 'package:finance_app/src/mixin/form_products_or_services_mixin.dart';
import 'package:finance_app/src/mixin/from_attach_mixin.dart';
import 'package:finance_app/src/mixin/init_edit_data_mixin.dart';
import 'package:finance_app/src/provider/dashboard/sales/sales_provider.dart';
import 'package:finance_app/src/provider/dashboard/sales/views/sales_invoice_provider.dart';
import 'package:finance_app/src/provider/dashboard/sales/views/sales_quotation_provider.dart';
import 'package:finance_app/src/provider/dashboard/sales/views/sales_summary_provider.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/cash_utils.dart';
import 'package:finance_app/src/utils/date_time_utils.dart';
import 'package:finance_app/src/utils/permission_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/calendar_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/currency_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/delete_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_tip_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/judge_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/list_select_bottom_sheet.dart';
import 'package:flutter/material.dart';

class NewInvoiceProvider extends BaseProvider<InvoiceDetailEntity>
    with
        FormProductsOrServicesMixin,
        FormAttachMixin,
        ApplyCreditMixin,
        InitEditDataMixin<InvoiceDetailEntity>,
        CheckPlanMixin {
  var poNumberController;
  TextEditingController invoiceNumberController = TextEditingController();
  FocusNode invoiceNumberFocusNode = FocusNode();

  DateTime dueDate;

  SettingDataCurrency currency;

  TextEditingController notesController = TextEditingController();
  FocusNode notesFocusNode = FocusNode();

  List<String> files = [];

  BusinessContactEntity client;

  QuotationEntity quotationNumber;

  PaymentTermEntity paymentTerm;

  List<String> menuList = [
    'Edit',
    'Delete',
    'Share',
  ];

  bool isSelectMode = false;

  bool isDelivered = false;

  @override
  void dispose() {
    super.dispose();
    invoiceNumberController.dispose();
    invoiceNumberFocusNode.dispose();
    notesController.dispose();
    notesFocusNode.dispose();
  }

  @override
  void initState() {
    if (id == null) {
      paymentTerm = Config.paymentTerm;
      initPaymentTerm();
    }
    super.initState();
  }

  void initPaymentTerm() {
    if (paymentTerm == null) return;
    switch (paymentTerm.paymentTermType) {
      case 'due_after':
        dueDate = date.add(Duration(days: paymentTerm.dueAfter));
        break;
      case 'due_on_receipt':
        dueDate = null;
        break;
      case 'custom':
        dueDate = null;
        break;
    }
  }

  void onSave() {
    unFocus();
    if (client == null) {
      showToast(S.current.please_select_client_name);
      return;
    }

    if (invoiceNumberController.text.length == 0) {
      showToast(S.current.please_enter_the_invoice_number);
      return;
    }

    if (paymentTerm == null) {
      showToast(S.current.please_select_payment_term);
      return;
    }

    if (date == null) {
      showToast(S.current.please_select_issue_date);
      return;
    }

    if (dueDate == null && paymentTerm.paymentTermType == 'custom') {
      showToast(S.current.please_select_due_date);
      return;
    }

    if (currency == null) {
      showToast(S.current.please_select_currency);
      return;
    }

    if (serviceAndProductList.length == 0) {
      showToast(S.current.please_add_item);
      return;
    }

    if (availableCredit > 0 && creditUsed == null && !isEdit) {
      obtainContext?.call((context) {
        onAskApplyCredit(onContinue: () {
          onSaveAPI();
        });
      });
      return;
    }
    onSaveAPI();
  }

  void onSaveAPI() async {
    List item = serviceAndProductList
        .map((e) => {
              'type': e.product != null && e.product.id != null
                  ? 'product'
                  : 'service',
              'type_id': e.id,
              'name': e.name,
              'quantity': e.number,
              'price': e.price,
              'is_tax_inclusive': e.isInclusive ? 1 : 0,
              'tax_id': e.tax.id,
              'tax_amount': e.taxAmount,
              'discount_type': e.discountType,
              'discount_value': e.discountValue,
            })
        .toList();
    List<int> attachmentIds = await uploadFiles();
    if (attachmentIds == null) return;

    InvoiceEntity orderEntity;
    if (isEdit == true) {
      await post(
          HttpInvoiceService.updateInvoice(data?.id,
              item: item,
              contact_id: client?.id,
              payment_term_id: paymentTerm?.id,
              total: totalDiscount,
              number: invoiceNumberController.text,
              subtotal: subTotal,
              currency_code: currency?.key,
              issued_at: DateTimeUtils.formatDateTime(date, 'yyyy-MM-dd'),
              due_at: DateTimeUtils.formatDateTime(dueDate, 'yyyy-MM-dd'),
              inclusive_tax_amount: includesGST,
              exclusive_tax_amount: gst,
              notes: notesController.text,
              // discount_type: discountType,
              // discount_amount: discountAmount,
              credit_used: creditUsed,
              attachment_ids: attachmentIds,
              quotation_id: quotationNumber.id,
              quotation_number: quotationNumber?.number),
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        orderEntity = getEntityFromResult<InvoiceEntity>(result);
      });
    } else {
      await post(
          HttpInvoiceService.createInvoice(
              item: item,
              contact_id: client?.id,
              payment_term_id: paymentTerm?.id,
              total: totalDiscount,
              number: invoiceNumberController.text,
              subtotal: subTotal,
              currency_code: currency?.key,
              issued_at: DateTimeUtils.formatDateTime(date, 'yyyy-MM-dd'),
              due_at: DateTimeUtils.formatDateTime(dueDate, 'yyyy-MM-dd'),
              inclusive_tax_amount: includesGST,
              exclusive_tax_amount: gst,
              notes: notesController.text,
              // discount_type: discountType,
              // discount_amount: discountAmount,
              credit_used: creditUsed,
              attachment_ids: attachmentIds,
              quotation_id: quotationNumber?.id,
              quotation_number: quotationNumber?.number),
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        orderEntity = getEntityFromResult<InvoiceEntity>(result);
      });
    }

    if (orderEntity != null) {
      updateDashboard();
      if (isSelectMode == true) {
        obtainContext?.call((context) {
          Navigator.of(context).pop(orderEntity);
        });
      } else {
        id = orderEntity.id;
        await post(editDataFuture,
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isEdit = false;
          data = transformData(
              getEntityFromResult<InvoiceDetailEntity>(result), false);
          update();
        });
      }
    }
  }

  void onChangeClientName() async {
    unFocus();
    final result = await Routes.navigateTo(
        RouterGenProviders.businessContactsPage,
        params: {
          'select': client,
        });
    if (result != null) {
      client = result;
      if (client.id != quotationNumber?.contactId) {
        quotationNumber = null;
      }
      creditValue = null;
      availableCredit = client.totalUnpaidCredit;
      currencyName = currency.key;
      update();
    }
  }

  void onChangeIssueDate() {
    unFocus();
    obtainContext?.call((context) async {
      DateTime newDate = await showCalendarBottomSheet(context, date);
      if (newDate != date) {
        date = newDate;
        initPaymentTerm();
        update();
      }
    });
  }

  void onChangeDueDate() {
    unFocus();
    obtainContext?.call((context) async {
      dueDate = await showCalendarBottomSheet(context, dueDate,
          startDate: date, withinFinancialYear: false);
      update();
    });
  }

  void onChangeCurrency() {
    unFocus();
    obtainContext?.call((context) async {
      currency = await showCurrencyBottomSheet(context, currency);
      update();
    });
  }

  void onChangeQuotationNumber() async {
    unFocus();
    final result = await Routes.navigateTo(
        RouterGenProviders.quotationNumberPage,
        params: {'select': quotationNumber, 'contactId': client?.id});
    if (result != null) {
      quotationNumber = result;
      if (client == null) client = quotationNumber.contact;
      serviceAndProductList =
          quotationNumber.items.map((e) => e.toServiceAndProduct()).toList();
      update();
    }
  }

  void onChangePaymentTerm() {
    obtainContext?.call((context) async {
      final result = await showPaymentTermBottomSheet(context, paymentTerm);
      if (result != null && result != paymentTerm) {
        paymentTerm = result;
        initPaymentTerm();
        update();
      }
    });
  }

  void unFocus() {
    invoiceNumberFocusNode.unfocus();
    notesFocusNode.unfocus();
  }

  @override
  Future get editDataFuture => HttpInvoiceService.showInvoice(id);

  void onSelectMenu(String value) {
    switch (value) {
      case 'Edit':
        onChangeEditMode();
        break;
      case 'Delete':
        onDelete();
        break;
      case 'See Payment':
        onSeePayment();
        break;
      case 'Receive Payment':
        onRecordPayment();
        break;
      case 'Share':
        onShare();
        break;
      case 'Mark As Delivered':
        onMarkAsDelivered();
        break;
    }
  }

  @override
  FutureOr<InvoiceDetailEntity> transformData(
      InvoiceDetailEntity d, bool isCache) {
    client = d.contact;
    invoiceNumberController.text = d.number;
    date = DateTimeUtils.toDateTime(d.issuedAt);
    dueDate = DateTimeUtils.toDateTime(d.dueAt);
    isDelivered = d.deliveredAt != null;
    // purchasesOrder = PurchaseOrderEntity()
    //   ..id = d.purchaseOrderId
    //   ..number = d.purchaseOrderNumber;
    // category = d.category;
    quotationNumber = QuotationEntity()
      ..id = d.quotationId
      ..number = d.quotationNumber;
    currency = Config.settingData.currency.firstWhere(
        (element) => element.key == d.currencyCode,
        orElse: () => null);
    paymentTerm = d.paymentTerm;
    serviceAndProductList =
        d.items.map((e) => e.toServiceAndProduct()).toList();
    notesController.text = d.notes;
    // if (d.discountType != null) {
    //   if (d.discountType == 'value') {
    //     discountValue = CashUtils.formatCashAmount2(d.discountAmount);
    //   } else {
    //     discountValue = '${d.discountAmount}%';
    //   }
    // }
    if (d.creditUsed != 0) {
      creditValue = CashUtils.formatCashAmount2(d.creditUsed);
    }
    if (d.status == 'closed') {
      menuList = [
        // 'Edit',
        'Share', 'See Payment'
      ];
    } else {
      menuList = ['Edit', 'Delete', 'Share', 'Receive Payment'];
      if (d.deliveredAt == null) {
        menuList.add('Mark As Delivered');
      }
      if (d.total != (d.unpaid + (creditUsed ?? 0))) {
        menuList.add('See Payment');
        menuList.remove('Delete');
        menuList.remove('Edit');
      }
    }
    // if (isDelivered) {
    //   menuList.remove('Edit');
    // }

    if (!Config.hasPermission(PermissionUtils.cashBank)) {
      menuList.remove('See Payment');
      menuList.remove('Receive Payment');
    }
    handleDataUpdate(d);
    return super.transformData(d, isCache);
  }

  void onDelete() {
    if (isPlanExpired()) return;
    obtainContext?.call((context) async {
      bool isSuccess = await showDeleteTransactionInfoBottomSheet(context);
      if (isSuccess == true) {
        isSuccess = false;
        await post(HttpInvoiceService.deleteInvoice(data.id),
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess == true) {
          await showDeleteSuccessTipBottomSheet(context);
          Navigator.of(context).pop({
            'delete': true,
          });
          updateDashboard();

          Config.dashboardProvider
              .onRefreshDashboard(refreshTransaction: false);
        }
      }
    });
  }

  void onRecordPayment() async {
    if (isPlanExpired()) return;

    List<InvoiceEntity> invoiceList = [
      InvoiceEntity()
        ..id = data.id
        ..issuedAt = data.issuedAt
        ..number = data.number
    ];
    final result =
        await Routes.navigateTo(RouterGenProviders.newCashInPage, params: {
      'invoiceList': invoiceList,
      'receiveFrom': client,
      'total': data.unpaid.toDouble(),
      'currency': currency,
      'isSelectMode': true,
    });
    if (result != null) {
      updateDashboard();
      onTry();
    }
  }

  void onSeePayment() async {
    await Routes.navigateTo(RouterGenProviders.seePaymentPage, params: {
      'invoiceId': data.id,
      'invoiceNumber': data.number,
    });
    loadData();
    updateDashboard();
  }

  void updateDashboard() {
    Providers.get<SalesProvider>(RouterGenProviders.salesPage, null)
        ?.getChildProvider<SalesQuotationProvider>()
        ?.onRefresh();
    Providers.get<SalesProvider>(RouterGenProviders.salesPage, null)
        ?.getChildProvider<SalesInvoiceProvider>()
        ?.onRefresh();
    Providers.get<SalesProvider>(RouterGenProviders.salesPage, null)
        ?.getChildProvider<SalesSummaryProvider>()
        ?.onRefresh();
  }

  void generate(QuotationDetailEntity generate) {
    if (generate == null) return;
    client = generate.contact;
    date = DateTimeUtils.toDateTime(generate.issuedAt);
    dueDate = DateTimeUtils.toDateTime(generate.dueAt);
    quotationNumber = QuotationEntity()
      ..id = generate.id
      ..number = generate.number;
    currency = Config.settingData.currency.firstWhere(
        (element) => element.key == generate.currencyCode,
        orElse: () => null);
    serviceAndProductList =
        generate.items.map((e) => e.toServiceAndProduct()).toList();
    notesController.text = generate.notes;
    // if (generate.discountType != null) {
    //   if (generate.discountType == 'value') {
    //     discountValue = CashUtils.formatCashAmount2(generate.discountAmount);
    //   } else {
    //     discountValue = '${generate.discountAmount}%';
    //   }
    // }
  }

  void onMarkAsDelivered() {
    if (isPlanExpired()) return;

    obtainContext?.call((context) async {
      bool isSuccess =
          await showMarkTheItemsAsDeliveredAsJudgeBottomSheet(context);
      if (isSuccess == true) {
        isSuccess = false;
        await post(HttpInvoiceService.deliver(id),
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess == true) {
          await showMarkedAsDeliveredSuccessTipBottomSheet(context);
          onTry();
        }
      }
    });
  }

  @override
  List<OcrAttachmentEntity> get attachments => data?.attachments;

  @override
  String get type => 'invoice';

  @override
  TextEditingController get numberCtl => invoiceNumberController;
}
